package com.googlecode.cswish.struts.spring;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * generate license through RSAEncrypt, MD5 etc
 * 
 * @see
 */
public class LicenseEncrypt {
	
	private final static Log logger = LogFactory.getLog(LicenseEncrypt.class);
	
	// at server
	private String pubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8zJcaPjNw01gBP6MpMn4qAe1lYIlPxC36q6FG2/jIfERE6Bwjx9ldv+vy1hEZuky9lP0U3oNQDOnofMQlugQ27pb2e74fR0FBvRfcFsm1TSNDgSqGOd2wuw/EGiJ+mLYACFXwmSL78iUZbomHJ00IVONJqs8fW/SoSzQ9jE4nRQIDAQAB";
	
	/**
	 * Main method for RSAEncrypt.
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		try {			
			LicenseEncrypt encrypt = new LicenseEncrypt();
			String priKey = null;
			String pubKey = null;
			
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
			keyPairGen.initialize(1024);
			KeyPair keyPair = keyPairGen.generateKeyPair();
			// Generate keys
			RSAPrivateKey keyPariePriKey = (RSAPrivateKey) keyPair.getPrivate();
			RSAPublicKey keyPariePubKey = (RSAPublicKey) keyPair.getPublic();
			
			priKey = Base64.encodeBase64String(keyPariePriKey.getEncoded());
			System.out.println(priKey);
			pubKey = Base64.encodeBase64String(keyPariePubKey.getEncoded());
			System.out.println(pubKey);
			
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			byte[] keyBytes = Base64.decodeBase64(priKey);
			PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(keyBytes);  
			PrivateKey privateKey = keyFactory.generatePrivate(priKeySpec);
			
			keyBytes = Base64.decodeBase64(pubKey);
			X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyBytes);  
            PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
			
			String encryptText = "encryptText";
			byte[] e = encrypt.encrypt(publicKey, encryptText.getBytes());
			byte[] de = encrypt.decrypt(privateKey, e);
			System.out.println(encrypt.bytesToString(e));
			System.out.println(encrypt.bytesToString(de));
		} catch (Exception e) {
			logger.error("run", e);
		}
	}

	/** */
	/**
	 * Change byte array to String.
	 * 
	 * @return byte[]
	 */
	protected String bytesToString(byte[] encrytpByte) {
		String result = "";
		for (Byte bytes : encrytpByte) {
			result += (char) bytes.intValue();
		}
		return result;
	}

	/** */
	/**
	 * Encrypt String.
	 * 
	 * @return byte[]
	 */
	private byte[] encrypt(PublicKey publicKey, byte[] obj) {
		if (publicKey != null) {
			try {
				Cipher cipher = Cipher.getInstance("RSA");
				cipher.init(Cipher.ENCRYPT_MODE, publicKey);
				return cipher.doFinal(obj);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	
	// text --> MD5 --> RSA encrypt --> mixed text
	public String encrypt(String text) {
		try {
			byte[] md5Bytes = MessageDigest.getInstance("MD5").digest(text.getBytes());
			
			// prepare enviroment
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			byte[] keyBytes = Base64.decodeBase64(pubKey);
			X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyBytes);  
            PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
			
            // encrypt it
            Cipher cipher = Cipher.getInstance("RSA");
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			byte[] e = cipher.doFinal(md5Bytes);
			
			return Base64.encodeBase64String(e) + '\n' + simpleEncrypt(text);
		} catch (Exception e) {
			logger.error("encrypt text", e);
			return null;
		}
	}
	
	public boolean encryptClass(String classFile, String destClassFile) {
		try {
			logger.debug("Read license class: " + classFile);
			logger.debug("Encrypt and output data file: " + destClassFile);
			
			byte[] data = FileUtils.readFileToByteArray(new File(classFile));
			String content = Base64.encodeBase64String(data);
			String encrypt = simpleEncrypt(content);
			FileUtils.write(new File(destClassFile), encrypt);
			return true;
		} catch (Exception e) {
			logger.error("encrypt class", e);
			return false;
		}
	}

	/** */
	/**
	 * Basic decrypt method
	 * 
	 * @return byte[]
	 */
	private byte[] decrypt(PrivateKey privateKey, byte[] obj) {
		if (privateKey != null) {
			try {
				Cipher cipher = Cipher.getInstance("RSA");
				cipher.init(Cipher.DECRYPT_MODE, privateKey);
				return cipher.doFinal(obj);
			} catch (Exception e) {
				logger.error("decrypt", e);
			}
		}
		return null;
	}
	
    private String simpleEncrypt(String text) throws UnsupportedEncodingException {  
        if (text == null)  
            return "";  
        if (text.length() == 0)  
            return "";  
  
        // NOTICE: it's same with LicenseDecrypt.simpleDecrypt
        String SEED = "0933912345463123732314329";
        int RADIX = 16;
        
        BigInteger bi_text = new BigInteger(text.getBytes("UTF-8"));  
        BigInteger bi_r0 = new BigInteger(SEED);  
        BigInteger bi_r1 = bi_r0.xor(bi_text);  
        return bi_r1.toString(RADIX);  
    }
}